iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 22
0
Mobile Development

Android 十全大補系列 第 22 篇

[Android 十全大補] Koin

  • 分享至 

  • xImage
  •  

Dagger 的強大跟好處相信大家可以慢慢能體會到,但實務上有時候我們不需要這麼強大的功能,或者是你覺得 dagger 學習曲線太陡了,這時候 koin 可能是另一個好選擇。

https://github.com/InsertKoinIO/koin

Koin

Koin 運用了大量的 kotlin 特性,讓 dependency injection 變成非常符合 kotlin 的風格,使用上也十分簡單。

對 koin 用到的特性有興趣的話,請參考:
Function literals with receiver: https://kotlinlang.org/docs/reference/lambdas.html#function-literals-with-receiver
Extensions: https://kotlinlang.org/docs/reference/extensions.html
Delegated Properties: https://kotlinlang.org/docs/reference/delegated-properties.html
Reified type parameters: https://kotlinlang.org/docs/reference/inline-functions.html

而效能跟其他相似 project 的 performance 比較起來也是蠻不錯的,一般使用可能不會有太大差別:

資料來源:https://medium.com/koin-developers/ready-for-koin-2-0-2722ab59cac3
可以看到 dagger setup 真的是超快的,因為使用 annotation processing,所有耗時的工作都在 compile 時做完了。

我們就一起來看 koin 有多麽簡潔優雅吧!

Install

跟以往一樣我們需要宣告 dependency 在 build.gradle:

dependencies {
    implementation "org.koin:koin-core:2.0.1"
    implementation "org.koin:koin-android:2.0.1"
}

Usage

我們一樣拿我們之前的 Printer 例子來舉例:

如果沒看過的話,請見:[Android 十全大補] Dagger

interface Wood

interface Water

interface Ink

interface Paper

interface Printer

class MyWood : Wood

class MyWater : Water

class MyInk(val water: Water) : Ink

class MyPaper(val wood: Wood) : Paper

class MyPrinter(val paper: Paper, val ink: Ink) : Printer

我們建立一個 module 來宣告所有可提供的 dependency,koin 提供了 single 跟 factory 二種方法,single 保證每次拿到的物件都是同一個,而 factory 則是每次呼叫都會有個新的物件。

val myModule = module {
    factory { MyPrinter(get(), get()) as Printer }
    single { MyWood() as Wood }
    single { MyWater() as Water }
    single { MyInk(get()) as Ink }
    single { MyPaper(get()) as Paper }
}

get 是個 reified type function,會自動轉換成需要的型別。
比如說 MyPrinter(get(), get()) 第一個 get 回傳的是 Paper 型別、而第二個 get 則會是 Ink 型別。

而 get 的值則會依照需要的型別找到相對應的建構 function,比如說 MyPrinter 的第一個 get 就會找到 MyPaper() as Paper。

as Paper 是不可省略的,因為 MyPaper 需要的型別是 Paper,而在 koin 的世界裡,Paper 跟 MyPaper 是互不相通的。

有了 modules 之後,在我們的 Andoid Application 啟動 koin 如下程式碼:

class MyApplication : Application() {
    override fun onCreate() {
        super.onCreate()
        startKoin {
            modules(myModule)
        }
    }
}

最後在 Activity 或是任何 context base 的 class 就可以用以下方式拿到 dependency 囉:

class MainActivity : AppCompatActivity() {

    val printer: Printer by inject()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val printer: Printer = get()

我們可以直接透過萬用的 get 直接拿到相對應型別的 dependency,或是使用 by inject() 做 lazy loading。是不是很簡單呢?

實際上 get 跟 inject 是 ComponentCallbacks 的 extension function,所以不只 context base,包含 Fragment 也可以使用喔!

挑戰:

  1. 請試著使用 Qualifier 解決同型別衝突
  2. 請把 PrinterModule 拆成多個 module

Pros & Cons

Koin 跟 dagger 比較起來的話,有以下幾個優點:

  • Fast compile
  • Easy to learn
  • Pure kotlin

而缺點的話:

  • Slow at setup
  • May cause RuntimeException

取捨就靠大家的智慧了,不知道大家比較喜歡哪個 library?

Android 十全大補已經正式出書上架囉!
有興趣的讀者歡迎參考:
https://www.tenlong.com.tw/products/9789864345786


上一篇
[Android 十全大補] Dagger as a Pro
下一篇
[Android 十全大補] MVVM
系列文
Android 十全大補 共 30 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

1 則留言

1
snowdaily
iT邦新手 5 級 ‧ 2020-12-22 12:59:33

而 get 的值則會依照需要的型別找到相對應的建構 function,比如說 MyPrinter 的第一個 get 就會找到 MyWood() as Wood。

as Wood 是不可省略的,因為 MyPrinter 需要的型別是 Wood,而在 koin 的世界裡,Wood 跟 MyWood 是互不相通的。


以上 MyPrinter 應該是 MyPaper?

Jintin iT邦新手 4 級 ‧ 2021-09-21 12:13:09 檢舉

是的 感謝糾錯

我要留言

立即登入留言